#include "ir_receive.h"
#include "RTC.h"
#include "misc.h"
#include "stdint.h"
#include "w55mh32_exti.h"
#include "w55mh32_gpio.h"
#include "w55mh32_rcc.h"
#include "w55mh32_tim.h"
#include <stdio.h>

static volatile uint8_t g_ir_state = IR_IDLE; //   ir receive status
static volatile uint32_t g_ir_last_time = 0;  // last time
static volatile uint16_t g_ir_data_count = 0; // ir count
static volatile uint8_t g_ir_data_ready = 0;  // data ready flag

// ir time data buff
static volatile uint32_t g_ir_time_data[IR_DATA_BUFFER_SIZE];

// Timer overflow count
static volatile uint32_t g_timer_overflow_count = 0;

/**
 * @brief  Timer overflow intr handler
 * @param  NULL
 * @retval NULL
 */
void tim4_irq_handler(void)
{
    if (TIM_GetITStatus(tim4, TIM_IT_Update) != reset)
    {
        // overflow count increase
        g_timer_overflow_count++;

        // clear intr flag
        TIM_ClearITPendingBit(tim4, TIM_IT_Update);
    }
}

/**
 * @brief   TIM 4Init
 * @param  system_clock_mhz: System clock frequency
 * @retval NULL
 */
static void tim4_init(uint32_t system_clock_mhz)
{
    // 使能TIM4时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, enable);

    // 配置TIM4
    TIM_TimeBaseInitTypeDef tim_time_base_structure;

    // 设置自动重装载值为最大值(16位)
    tim_time_base_structure.TIM_Period = 0xFFFF; // 16位最大计数值

    // 设置预分频系数，得到1MHz时钟频率(1us精度)
    tim_time_base_structure.TIM_Prescaler = system_clock_mhz - 1;

    // 设置时钟分割
    tim_time_base_structure.TIM_ClockDivision = TIM_CKD_DIV1;

    // 设置计数器模式
    tim_time_base_structure.TIM_CounterMode = TIM_CounterMode_Up;

    // 初始化TIM4
    TIM_TimeBaseInit(tim4, &tim_time_base_structure);

    // 使能更新中断
    TIM_ITConfig(tim4, TIM_IT_Update, enable);

    // 配置NVIC
    NVIC_InitTypeDef nvic_init_structure;
    nvic_init_structure.NVIC_IRQChannel = tim4_irqn;
    nvic_init_structure.NVIC_IRQChannelPreemptionPriority = 3;
    nvic_init_structure.NVIC_IRQChannelSubPriority = 0;
    nvic_init_structure.NVIC_IRQChannelCmd = enable;
    NVIC_Init(&nvic_init_structure);

    // 使能TIM4
    TIM_Cmd(tim4, enable);
}

/**
 * @brief  get 32-bit time values (including overflow counts)
 * @retval 32-bit time values(us)
 */
uint32_t get_32bit_timer_value(void)
{
    return (g_timer_overflow_count << 16) | TIM_GetCounter(tim4);
}

/**
 * @brief  exti0 intr handler
 */
void exti0_irq_handler(void)
{
    uint32_t current_time = 0;
    uint32_t time_interval = 0;

    if (EXTI_GetITStatus(IR_EXTI_LINE) != reset)
    {
        // get current time
        current_time = get_32bit_timer_value();

        // Calculate the time interval
        time_interval = current_time - g_ir_last_time;

        // If it is the first data point or the receiving status, record the time
        if (g_ir_state == IR_IDLE)
        {
            // The first edge begins to receive data
            g_ir_state = IR_RECEIVING;
            g_ir_data_count = 0;
            g_ir_data_ready = 0;
        }

        // if the buffer is not full then Store time data
        if (g_ir_data_count < IR_DATA_BUFFER_SIZE)
        {
            g_ir_time_data[g_ir_data_count] = time_interval;
            g_ir_data_count++;
        }

        // Update time
        g_ir_last_time = current_time;

        // Clear the interrupt flag
        EXTI_ClearITPendingBit(IR_EXTI_LINE);
    }
}

/**
 * @brief  get the number of received data
 * @param  None
 * @retval the number of received data
 */
uint8_t ir_get_data_count(void)
{
    return g_ir_data_count;
}

/**
 * @brief  get infrared time data
 * @param  time_buffer: Point to the buffer that stores time data
 * @param  count: number of time data
 * @retval None
 */
static void ir_get_time_data(uint16_t *time_buffer, uint8_t count)
{
    uint16_t i = 0;

    // 复制数据到用户缓冲区
    for (i = 0; i < count && i < IR_DATA_BUFFER_SIZE && i < g_ir_data_count; i++)
    {
        time_buffer[i] = g_ir_time_data[i];
    }
}

/**
 * @brief  clear recieve data
 * @param  None
 * @retval None
 */
void ir_clear_data(void)
{
    g_ir_data_count = 0;
    g_ir_data_ready = 0;
    g_ir_state = IR_IDLE;
    g_ir_last_time = get_32bit_timer_value();
}

/**
 * @brief  check data is ready
 * @retval 1:ready 0:not ready
 */

uint32_t g_last_activity_time = 0;
uint16_t g_previous_count = 0;
uint8_t ir_is_ready(void)
{
    // If data is being received and there is data
    if (g_ir_state == IR_RECEIVING && g_ir_data_count > 0)
    {
        // Use global static variables to track timeouts
        g_last_activity_time = 0;
        g_previous_count = 0;
        uint32_t current_time = get_32bit_timer_value();

        // If the data_Count changes, it indicates that new data has arrived
        if (g_previous_count != g_ir_data_count)
        {
            g_previous_count = g_ir_data_count;
            g_last_activity_time = current_time;
        }
        else
        {
            // The data_Count has not changed. Check if it has timed out
            uint32_t time_since_last_activity = current_time - g_last_activity_time;

            // If there is no new data within 10ms, it is considered that the reception is complete
            if (time_since_last_activity > 10000) // 10ms = 10000us
            {
                // Set the data ready flag
                g_ir_data_ready = 1;
                g_ir_state = IR_IDLE;
                // Reset the tracking variable
                g_previous_count = 0;
                return 1;
            }
        }
        return 0;
    }

    return 0;
}

/**
 * @brief  Configure external interrupts
 * @param  None
 * @retval None
 */
static void ir_receive_config(void)
{
    // Enable the AFIO clock
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

    // Connect the EXTI line to the GPIO
    GPIO_EXTILineConfig(IR_PORT_SOURCE, IR_PIN_SOURCE);

    // Configure the EXTI line
    EXTI_InitTypeDef exti_init_structure;
    exti_init_structure.EXTI_Line = IR_EXTI_LINE;
    exti_init_structure.EXTI_Mode = EXTI_Mode_Interrupt;
    exti_init_structure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
    exti_init_structure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&exti_init_structure);

    // Configure NVIC
    NVIC_InitTypeDef nvic_init_structure;
    nvic_init_structure.NVIC_IRQChannel = IR_NVIC_IRQ_Channel;
    nvic_init_structure.NVIC_IRQChannelPreemptionPriority = 2;
    nvic_init_structure.NVIC_IRQChannelSubPriority = 0;
    nvic_init_structure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&nvic_init_structure);

    // Clear the interrupt flag bit
    EXTI_ClearITPendingBit(IR_EXTI_LINE);
}

/**
 * @brief  Infrared reception initialization
 * @param  None
 * @retval None
 */
void ir_receive_init(void)
{
    // Enable the GPIO clock
    RCC_APB2PeriphClockCmd(IR_RCC_APB2_PERIPH, ENABLE);

    // Configure GPIO to float input mode
    GPIO_InitTypeDef gpio_init_structure;
    gpio_init_structure.GPIO_Pin = IR_PIN;
    gpio_init_structure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    gpio_init_structure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(IR_PORT, &gpio_init_structure);

    // Configure external interrupts (triggered by both edges)
    ir_receive_config();

    tim4_init(216);

    // init time
    g_ir_last_time = get_32bit_timer_value();
}

unsigned short g_receive_data[3][IR_DATA_BUFFER_SIZE];
unsigned short g_filtering_data[IR_DATA_BUFFER_SIZE];

/**
 * @brief  Mean filtering processing
 * @param  data_Count: The number of valid data
 * @retval None
 */
static void ir_mean_filter(uint8_t data_count)
{
    uint16_t i = 0;
    uint32_t sum = 0;
    uint16_t mean_value = 0;

    for (i = 0; i < data_count && i < IR_DATA_BUFFER_SIZE; i++)
    {
        sum = 0;

        for (unsigned char j = 0; j < 3; j++)
        {
            sum += g_receive_data[j][i];
        }

        mean_value = (uint16_t)(sum / 3);

        g_filtering_data[i] = mean_value;
    }
}

void ir_learn()
{
    // count
    unsigned char count = 0;
    unsigned char success_flag = 0;
    unsigned int data_count = 0;

    ir_get_time_data(g_receive_data[count], ir_get_data_count());
    // get the number of ir data
    data_count = ir_get_data_count();
    ir_clear_data();
    count++;

    // Start the 30-second timer
    RTC_30Sec_Start();

    while (!RTC_30Sec_IsTimeout())
    {
        if (ir_is_ready())
        {
            ir_get_time_data(g_receive_data[count], ir_get_data_count());
            ir_clear_data();
            count++;

            if (count == 3)
            {
                success_flag = 1;
                break;
            }
        }
    }

    RTC_30Sec_Stop();

    if (!success_flag)
    {
        // Timeout, stop infrared learning and return
        printf("timeout return \r\n");
        return;
    }

    // Mean Filter
    ir_mean_filter(data_count);

    printf("IR learn success \r\n");
    printf("length:%d,Infrared time series:\r\n", data_count);
    for (unsigned int i = 1; i < data_count; i++)
    {
        printf("%d\r\n", g_filtering_data[i]);
    }
    printf("\r\n");
}
